﻿using System;
using System.Collections.Generic;
using OverStore.Exceptions;
using OverStore.Runtime;
using OverStore.Scoping;

namespace OverStore
{

    /// <summary>
    /// Provide operations for loading and storaging objects,
    /// collections and set of objects.
    /// </summary>
    /// <remarks>
    /// <para>
    /// IPersistenceSession exposes set of methods allows to load, save, mark as delete, add, attach and get by identifier instance of any type;
    /// and also read set of objects from data storage. These operations are named persistence operations.
    /// </para>
    /// <para>
    /// For successfuly completion any persistence operation, persistence session must has properly configured object repository 
    /// (OverStore.Runtime.Repository.IRepository&lt;TInstance&gt; and OverStore.Runtime.Repository.IRegistry&lt;TInstance, TIdentifier&gt;);
    /// and optional object reader OverStore.Runtime.ObjectReader.ObjectReader&lt;TItem&gt; required to use IPersistenceSession.CreateReader&lt;TItem&gt; method.
    /// </para>
    /// <para>
    /// Note that by default object repository is selected using exact match of type argument of persistence operation with object repository type.
    /// This behavior may be changed by providing custom OverStore.Runtime.PersistenceServiceProvider.IPersistenceServiceProvider implementation,
    /// or by specifying additional object repositories using OverStore.Runtime.Repository.InheritanceRepository&lt;TInstance, TIdentifier&gt; or 
    /// OverStore.Runtime.Repository.RedirectRepository&lt;TInstance, TIdentifier, TRedirectToInstance, TRedirectToIdentifier&gt;.
    /// RedirectRepository allows to persist specific type using repository for another compatible (assignable) type.
    /// InheritanceRepository allows to persist set of types inherited from specific type. 
    /// Actual repository for persist specific instance is defined by actual runtime instance type.
    /// </para>
    /// <para>
    /// IPersistenceSession does not use transactions explicity. For control transactions, use OverStore.Scoping.OperationScope class. 
    /// The way how persistence session uses transaction is depends on OverStore.Runtime.Storage.IDataStoraging implementation. 
    /// </para>
    /// </remarks>
    public interface IPersistenceSession : IDisposable
    {
        /// <summary>
        /// <para>
        /// Reloads object data, including object's aggregates, and reject all changes made to object. 
        /// If object was marked as deleted, the deletion mark is cleared.
        /// </para>
        /// <para>
        /// Method returns <c>true</c> if instance data was successfuly reloaded, <c>false</c> if there are no object data in data storage.</para>
        /// <para>
        /// If instance was not registered in the session before calling the method and method throws exception or returns <c>false</c>
        /// (it means there are no data for instance in database) instance will not be registered in session.
        /// It prevents saving uneccessary entities due calling SaveAll() method.
        /// </para>
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance to reload.</typeparam>
        /// <param name="instance">The instance to reload.</param>
        /// <exception cref="System.ArgumentNullException"><paramref name="instance"/> is null reference.</exception>
        /// <exception cref="TypePersistenceNotSupportedException">The persistence of specific type not supported.</exception>
        /// <exception cref="PersistenceOperationFailedException">The error is occured during data source operation</exception>
        /// <exception cref="IdentityReferenceIntegrityException">Different instance with same identifier already registered in session.</exception>
        /// <exception cref="ObjectDisposedException">Object is disposed.</exception>
        bool Reload<TInstance>(TInstance instance) where TInstance : class;

        /// <summary>
        /// Clears specified collection and fills it up with object are read from parameterless reader.
        /// An item of collection is not reloaded.
        /// </summary>
        /// <typeparam name="TInstance">The type of the item of collection to reload.</typeparam>
        /// <param name="collection">The collection to reload.</param>
        /// <exception cref="System.ArgumentNullException"><paramref name="collection"/> is null reference.</exception>
        /// <exception cref="TypePersistenceNotSupportedException">The persistence of specific type not supported.</exception>
        /// <exception cref="PersistenceOperationFailedException">The error is occured during data source operation</exception>
        /// <exception cref="IdentityReferenceIntegrityException">Different instance with same identifier already registered in session.</exception>
        /// <exception cref="ObjectDisposedException">Object is disposed.</exception>
        void ReloadCollection<TInstance>(IEnumerable<TInstance> collection) where TInstance : class;

        /// <summary>
        /// Clears specified collection and fills it up with object are read from parameterized reader with specified parameters.
        /// An item of collection is not reloaded.
        /// </summary>
        /// <typeparam name="TInstance">The type of the item of collection to reload.</typeparam>
        /// <typeparam name="TParameter">The type of the parameter.</typeparam>
        /// <param name="collection">The collection to reload.</param>
        /// <param name="parameter">The parameter.</param>
        /// <exception cref="System.ArgumentNullException"><paramref name="collection"/> is null reference.</exception>
        /// <exception cref="TypePersistenceNotSupportedException">The persistence of specific type not supported.</exception>
        /// <exception cref="PersistenceOperationFailedException">The error is occured during data source operation</exception>
        /// <exception cref="IdentityReferenceIntegrityException">Different instance with same identifier already registered in session.</exception>
        /// <exception cref="ObjectDisposedException">Object is disposed.</exception>
        void ReloadCollection<TInstance, TParameter>(IEnumerable<TInstance> collection, TParameter parameter)
            where TInstance : class
            where TParameter : class;

        /// <summary>
        /// <para>
        /// Saves object changes into data storage, also saves object's aggregates, and accept object changes.
        /// </para>
        /// <para>
        /// Calling this method with instance was not registered in session before registers the instance if 
        /// method call was successful. If method throws exception, the instance will not be registered.
        /// Instance already registered in session still registered even if Save method throws exception.
        /// </para>
        /// </summary>
        /// <typeparam name="TInstance">Type of object to save.</typeparam>
        /// <param name="instance">Instance to save.</param>
        /// <exception cref="System.ArgumentNullException"><paramref name="instance"/> is null reference.</exception>
        /// <exception cref="TypePersistenceNotSupportedException">The persistence of specific type is not supported.</exception>
        /// <exception cref="PersistenceOperationFailedException">The error is occured during data source operation</exception>
        /// <exception cref="VersionConflictException">The store of object failed because newest version of object already exist in data storage.</exception>
        /// <exception cref="IdentityReferenceIntegrityException">Different instance with same identifier already registered in session.</exception>
        /// <exception cref="ObjectDisposedException">Object is disposed.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException">The saving of instance of specified type is not supported by repository.</exception>
        /// <remarks>
        /// <para>
        /// Entity aggregates (other entities, which are depends on entity being saved) are saved in any case.
        /// Entity references (other entites are referenced by entity being saved) are saved only if entity is changed.
        /// </para>
        /// </remarks>
        void Save<TInstance>(TInstance instance) where TInstance : class;

        /// <summary>
        /// Save changes (including aggregates) of each collection item into database. If some items were deleted from collection, that items
        /// will be deleted from database.
        /// </summary>
        /// <typeparam name="TInstance">Collection item type.</typeparam>
        /// <param name="collection">The collection to save.</param>
        /// <exception cref="System.ArgumentNullException"><paramref name="collection"/> is null reference.</exception>
        /// <exception cref="TypePersistenceNotSupportedException">The persistence of specific type is not supported.</exception>
        /// <exception cref="PersistenceOperationFailedException">The error is occured during data source operation</exception>
        /// <exception cref="VersionConflictException">The storing of instance failed because newest version of object already exist in data storage.</exception>
        /// <exception cref="IdentityReferenceIntegrityException">Different instance with same identifier already registered in session.</exception>
        /// <exception cref="ObjectDisposedException">Object is disposed.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException"> Specified collection type is not supported.</exception>
        void SaveCollection<TInstance>(IEnumerable<TInstance> collection) where TInstance : class;

        /// <summary>
        /// Save changes of all object in session into storage and accept all changes.
        /// Allows Unit Of Work scenario for working with session.
        /// </summary>
        /// <exception cref="PersistenceOperationFailedException">The error is occured during data source operation</exception>
        /// <exception cref="VersionConflictException">The store of object failed because newest version of object already exist in data storage.</exception>
        /// <exception cref="ObjectDisposedException">Object is disposed.</exception>
        void SaveAll();

        /// <summary>
        /// <para>
        /// Marks object as deleted. Object will be deleted from data storage during Save method call.
        /// For cancel deletion mark, use Reload method.
        /// </para>
        /// <para>
        /// The object still available by it's identifier due all session lifetime, even
        /// after it is actually deleted from data storage. 
        /// If object leaves in storage after deleting (for example if entity deleting 
        /// updates special column in table ("soft deleting")) the object will be available 
        /// by it's identifier even in another session.</para>
        /// <para>
        /// If entity was not registered in session before calling the method, method throws <see cref="PersistenceOperationFailedException"/>.
        /// To mark instance as deleted, you need to load instance before (for example using GetByIdentifier method).
        /// </para>
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <param name="instance">The instance to mark it as deleted.</param>
        /// <exception cref="System.ArgumentNullException"><paramref name="instance"/> is null reference.</exception>
        /// <exception cref="TypePersistenceNotSupportedException">The persistence of specific type not supported.</exception>
        /// <exception cref="ObjectDisposedException">Object is disposed.</exception>
        /// <exception cref="IdentityReferenceIntegrityException">Different instance with same identifier already registered in session.</exception>
        /// <exception cref="PersistenceOperationFailedException">Error occured during mark object as deleted. Entity was not registered in session previously.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException">The marking instance of specified type as deleted is not supported by repository.</exception>
        void MarkAsDeleted<TInstance>(TInstance instance)
            where TInstance : class;

        /// <summary>
        /// Registers instance in session.
        /// The object become available by it's identifier.
        /// If different instance with equal identifier already exists in session,
        /// the <see cref="T:OverStore.Exceptions.IdentityReferenceIntegrityException"/> will be thrown.
        /// If this instance already registered, method does not perform any action.
        /// <para>If instance is marked as deleted, method clear deleting mark.</para>
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance to register in session.</typeparam>
        /// <param name="instance">The instance to register in session.</param>
        /// <exception cref="System.ArgumentNullException"><paramref name="instance"/> is null.</exception>
        /// <exception cref="TypePersistenceNotSupportedException">The persistence of specific type not supported.</exception>
        /// <exception cref="IdentityReferenceIntegrityException">The different instance of same type with equal key already exists in session.</exception>
        /// <exception cref="ObjectDisposedException">Object is disposed.</exception>
        /// <exception cref="PersistenceOperationFailedException">Error occured during add object.</exception>
        void Add<TInstance>(TInstance instance) where TInstance : class;

        /// <summary>
        /// Joins specified object or collection to session and apply changes made to <paramref name="instance"/>.
        /// </summary>
        /// <typeparam name="TInstance">OverrideDbType of object to attach.</typeparam>
        /// <param name="instance">Instance for attach to session.</param>
        /// <returns>
        /// Instance registered in session and contains applied changes.
        /// </returns>
        /// <exception cref="ArgumentNullException">Instance is null.</exception>
        /// <exception cref="TypePersistenceNotSupportedException">The persistence of specific type not supported.</exception>
        /// <exception cref="PersistenceOperationFailedException">The error is occured during data source operation</exception>
        /// <exception cref="ObjectDisposedException">Object is disposed.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException">The attaching is not supported by repository.</exception>
        TInstance Attach<TInstance>(TInstance instance)
            where TInstance : class;

        /// <summary>
        /// Get instance with specified identifier. If instance with such identifier is not loaded yet, it will be
        /// loaded before return.
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <typeparam name="TIdentifier">The type of the identifier.</typeparam>
        /// <param name="objectIdentifier">The object identifier.</param>
        /// <returns>
        /// The instance of <typeparamref name="TInstance"/> type with specified indentifier.
        /// </returns>
        /// <exception cref="System.ArgumentNullException"><paramref name="objectIdentifier"/> is null reference.</exception>
        /// <exception cref="TypePersistenceNotSupportedException">The persistence of TInstance type is not supported.</exception>
        /// <exception cref="IdentifierTypeIsInvalidException">OverrideDbType of identifier is invalid.</exception>
        /// <exception cref="PersistenceOperationFailedException">The error is occured during data source operation</exception>
        /// <exception cref="ObjectDisposedException">Object is disposed.</exception>
        /// <remarks>If object supports deffered loading, some of exception may be thrown when accessing object data.</remarks>
        TInstance GetByIdentifier<TInstance, TIdentifier>(TIdentifier objectIdentifier)
            where TInstance : class;

        /// <summary>
        /// Get entity with specified identifier from data storage. If entity was requested before, it will be returned,
        /// otherwise entity with specified identifier will be loaded from data storage and returned.
        /// If there are no data for entitiy with such identifier in data storage, method returns null.
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <typeparam name="TIdentifier">The type of the identifier.</typeparam>
        /// <param name="objectIdentifier">The object identifier.</param>
        /// <returns>
        /// The instance of <typeparamref name="TInstance"/> type with specified indentifier.
        /// </returns>
        /// <exception cref="System.ArgumentNullException"><paramref name="objectIdentifier"/> is null reference.</exception>
        /// <exception cref="TypePersistenceNotSupportedException">The persistence of TInstance type is not supported.</exception>
        /// <exception cref="IdentifierTypeIsInvalidException">OverrideDbType of identifier is invalid.</exception>
        /// <exception cref="PersistenceOperationFailedException">The error is occured during data source operation</exception>
        /// <exception cref="ObjectDisposedException">Object is disposed.</exception>
        /// <remarks>If object supports deffered loading, some of exception may be thrown when accessing object data.</remarks>
        TInstance GetByIdentifierOrDefault<TInstance, TIdentifier>(TIdentifier objectIdentifier)
            where TInstance : class;

        /// <summary>
        /// <para>
        /// Creates iterator throught collection of object in storage with specified parameter. Object reader provides sequential forward-only
        /// access to set of objects.</para>
        /// <para>
        /// Note object readers always opens in current operation scope. Reader itselfs does not open or close any operation scopes.</para>
        /// </summary>
        /// <typeparam name="TItem">The type of the item to iterate.</typeparam>
        /// <typeparam name="TParameter">The type of the parameter uses for load object set from data storage.</typeparam>
        /// <param name="parameter">The parameter uses for load object set from data storage.</param>
        /// <returns>
        /// The iterator throught set of object from data storage.
        /// </returns>
        /// <exception cref="System.ArgumentNullException"><paramref name="parameter"/> is null reference.</exception>
        /// <exception cref="ParameterTypeNotSupportedException">Parameters has an unsupported type</exception>
        /// <exception cref="TypePersistenceNotSupportedException">The reading of objects of specified type is not supported.</exception>
        /// <exception cref="PersistenceOperationFailedException">The error is occured during data source operation</exception>
        /// <exception cref="ObjectDisposedException">Object is disposed.</exception>
        IEnumerable<TItem> CreateReader<TItem, TParameter>(TParameter parameter)
            where TItem : class
            where TParameter : class;

        /// <summary>
        /// <para>
        /// Creates iterator throught collection of object in storage with specified parameter. Object reader provides sequential forward-only
        /// access to set of objects.
        /// </para>
        /// <para>
        /// Note object readers always opens in current operation scope. Reader itselfs does not open or close any operation scopes.</para>
        /// </summary>
        /// <typeparam name="TItem">The type of the item to iterate.</typeparam>.
        /// <returns>
        /// The iterator throught set of object from data storage.
        /// </returns>
        /// <exception cref="System.ArgumentNullException">Object type is null.</exception>
        /// <exception cref="System.ArgumentNullException">Parameters is null.</exception>
        /// <exception cref="ParameterTypeNotSupportedException">Parameters has an unsupported type</exception>
        /// <exception cref="TypePersistenceNotSupportedException">The reading of objects of specified type is not supported.</exception>
        /// <exception cref="PersistenceOperationFailedException">The error is occured during data source operation</exception>
        /// <exception cref="ObjectDisposedException">Object is disposed.</exception>
        IEnumerable<TItem> CreateReader<TItem>()
            where TItem : class;

        /// <summary>
        /// Starts operation scope for the current session.
        /// </summary>
        OperationScope OpenScope();
    }
}